package com.tmt.jpafieldvalueconvert;

import com.tmt.annotation.parser.LazyLoadParser;
import com.tmt.helper.PackageHelper;
import com.tmt.helper.ReflectHelper;
import com.tmt.jpafieldvalueconvert.impl.DefaultConverterLoader;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.persistence.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;

/**
 * Created by ThreeManTeam on 2017/5/22.
 */

public class JpaFieldValueConvertListener {

	private static HashMap<String, IJpaFieldValueConverter> jpaFieldValueConverterHashMap = new HashMap<>();

	@PrePersist
	public <T> void entityPrePersist(T obj) {
		convertToDatabaseColumn(obj, PrePersist.class);
	}

	@PostPersist
	public <T> void entityPostPersist(T obj) {
		convertToEntityAttribute(obj, PostPersist.class);
	}

	@PostLoad
	public <T> void entityPostLoad(T obj) {
		convertToEntityAttribute(obj, PostLoad.class);
	}

	@PreUpdate
	public <T> void entityPreUpdate(T obj) {
		convertToDatabaseColumn(obj, PreUpdate.class);
	}

	@PostUpdate
	public <T> void entityPostUpdate(T obj) {
		convertToEntityAttribute(obj, PostUpdate.class);
	}

	@PreRemove
	public <T> void entityPreRemove(T obj) {
		convertToDatabaseColumn(obj, PreRemove.class);
	}

	@PostRemove
	public <T> void entityPostRemove(T obj) {
		convertToEntityAttribute(obj, PostRemove.class);
	}

	@PreDestroy
	public <T> void entityPreDestroy(T obj) {
		convertToDatabaseColumn(obj, PreDestroy.class);
	}

	@PostConstruct
	public <T> void entityPostConstruct(T obj) {
		convertToEntityAttribute(obj, PostConstruct.class);
	}

	private <T> void convertToEntityAttribute(T obj, Class eventCls) {
		List<Field> fields = ReflectHelper.findAllFieldByAnnotation(obj.getClass(), JpaFieldValueConvert.class, -1);
		fields.forEach(x -> {
			try {
				JpaFieldValueConvert annotation = x.getAnnotation(JpaFieldValueConvert.class);
				IJpaFieldValueConverter iConverter = getConverterInstance(obj, x.getName(), eventCls.getSimpleName(),
						annotation.converter(), getPackageName(obj, annotation));
				if (iConverter != null)
					iConverter.convertToEntityAttribute(obj, x.getName(), eventCls.getSimpleName());
			} catch (Exception ex) {
			}
		});
	}

	private <T> void convertToDatabaseColumn(T obj, Class eventCls) {
		List<Field> fields = ReflectHelper.findAllFieldByAnnotation(obj.getClass(), JpaFieldValueConvert.class, -1);
		fields.forEach(x -> {
			try {
				JpaFieldValueConvert annotation = x.getAnnotation(JpaFieldValueConvert.class);
				IJpaFieldValueConverter iConverter = getConverterInstance(obj, x.getName(), eventCls.getSimpleName(),
						annotation.converter(), getPackageName(obj, annotation));
				if (iConverter != null)
					iConverter.convertToDatabaseColumn(obj, x.getName(), eventCls.getSimpleName());
			} catch (Exception ex) {
			}
		});
	}

	private static <T> String getPackageName(T obj, JpaFieldValueConvert annotation) {
		String packageName = annotation.packageName();
		if (StringUtils.isEmpty(packageName)) {
			packageName = annotation.converter().getPackage().getName();
			if (obj != null) {
				packageName = obj.getClass().getPackage().getName();
				int index = packageName.lastIndexOf('.');
				if (index > 0) {
					packageName = packageName.substring(0, index);
				}
			}
		}
		return packageName;
	}

	private static <T, Y extends IJpaFieldValueConverter> IJpaFieldValueConverter getConverterInstance(T obj, String propName, String eventName,
																									   Class<Y> converter, String packageName) {
		IJpaFieldValueConverter iConverter = null;
		try {
			String key = LazyLoadParser.getValidSimpleClassName(obj.getClass()) + "." + propName + "." + eventName;
			if (jpaFieldValueConverterHashMap.containsKey(key)) {
				iConverter = jpaFieldValueConverterHashMap.get(key);
				return iConverter;
			}

            if (Modifier.isAbstract(converter.getModifiers())) {
                List<String> clsNames = PackageHelper.getClassName(packageName, true, true, 1);
                for (String clsName : clsNames) {
                    Class cls = Class.forName(clsName);
                    if (converter.isAssignableFrom(cls) && !Modifier.isAbstract(cls.getModifiers())) {
                        iConverter = DefaultConverterLoader.getInstance().loadConverter(cls);
                        if (iConverter.support(obj, propName, eventName)) {
                            jpaFieldValueConverterHashMap.put(key, iConverter);
                            break;
                        }
                    }
                }
            }else{
                iConverter = DefaultConverterLoader.getInstance().loadConverter(converter);
                if (iConverter.support(obj, propName, eventName)) {
                    jpaFieldValueConverterHashMap.put(key, iConverter);
                }else{
                    iConverter = null;
                }
            }

		} catch (Exception ex) {
		}
		return iConverter;
	}

}
